package com.geek.bams.service;

import com.geek.bams.dao.*;
import com.geek.bams.entity.*;
import com.geek.bams.exception.*;
import com.geek.bams.vo.RankVO;

import javax.swing.*;
import java.util.List;
import java.util.TreeMap;
import java.util.TreeSet;

/**
 * @Desc 业务逻辑层
 * @Author wangpeng
 * @Date 2025/10/13
 */
public class Bank {

    /*//1.最初使用数组存储（在内存中），关联ArrayDao,
    private ArrayDao dao;*/

     /*//2.可插拔式编程（在内存中），面向接口编程
    private IDao<Account , Account[]> dao;*/

    //3.使用集合存储（在内存中），
    private IDao<Account , List<Account>> dao;




    /**这个单例，是解决
     * Bank bank1 = new Bank();
     * Bank bank2 = new Bank();
     * //代表不同银行
     * //开户
     * Account acc1 = bank1.register("123" , "123" , "李四" ,"123" ,"123@qq.com",0 );
     * Account acc2 = bank1.register("111" , "111" , "张三" ,"111" ,"111@qq.com",1 );
     * //登录
     * Account acc1Login = bank2.login(1L, "123");
     * Account acc2Login = bank2.login(2L, "111");
     * 在bank1中开户，在bank2中登录，则会登录失败！！！
     *原因：每new一次，相当于创建一个新的Bank实例，那dao里的也是新的实例的数据，那里面的Account[]数组也是新的实例。
     */
    //如果需要bank保证是同一实例，则使用单例
    //私有的 静态的  本类的实例
    //private static Bank bank;
    private static Bank instance;

    //私有的 构造方法
    //不再允许，外部通过构造方法创建实例，只能通过getInstance()获取，因此使用单例后，就不能再使用new的方式创建bank实例
    private Bank(){
        //dao = new ArrayDao();
        //dao = new ListDao();
        //dao = new FileDao();
        dao=new MyBatisDao();
    }

    //公开的 静态的  获取本类实例的方法
    /*public static Bank getInstance(){
        if(bank==null){
            bank = new Bank();
        }
        return bank;
    }*/

    public static Bank getInstance(){
        if(instance ==null){
            instance = new Bank();
        }
        return instance;
    }
    /**bank实例处理后，这样getInstance()就都是同一个实例，无论bank3,4...都是同一个实例
     * Bank bank1 = Bank.getInstance();
     * Bank bank2 = Bank.getInstance();
     */

    /**
     * 用户开户
     * @param password   账户密码
     * @param repassword 重复密码
     * @param name       真实姓名
     * @param personID   身份证号
     * @param email      邮箱
     * @param type       选择账户类型  项目需求规定账户类型：0 – 储蓄账户  1 – 信用账户 2 – 可贷款储蓄账户 3– 可贷款信用账户
     * @return  银行账户
     */
    public Account register(String password, String repassword, String name, String personID, String email, int type)
            throws NotFoundException, TypeException, RegisterException {
        Account acc = null;
        if(password==null || password.equals("") || repassword==null || repassword.equals("") ||
                name==null || name.equals("") || personID==null || personID.equals("")){
            /*System.out.println("账户信息不能为空！");
            return null;*/
            throw new NotFoundException("用户ID和密码不能为空");

        }

        if(password.equals(repassword)){
            //生成ID
            Long id = dao.createID();
            if(id<=0){
                /*System.out.println("ID生成有误");
                return null;*/
                throw new RegisterException("ID生成有误!");
            }

            //多态：父类的引用指向子类的实例
            switch (type){
                case 0:  //储蓄账户
                    acc = new SavingAccount(id , name , password , personID , email , 0.0);
                    break;
                case 1:  //信用账户
                    acc = new CreditAccount(id , name , password , personID , email , 0.0 , 1500.0);
                    break;
                case 2:  //可贷款储蓄账户
                    acc = new LoanSavingAccount(id ,name, password , personID , email ,0.0 , 0L);
                    break;
                case 3:  //可贷款信用账户
                    acc = new LoanCreditAccount(id ,name, password , personID , email ,0.0 , 1500.0 , 0L);
                    break;
                default:
                    /*System.out.println("账户类型未匹配！");*/
                    throw new TypeException("账号类型不匹配");
            }

            //注册账户信息
            if(acc!=null){
                boolean flag = dao.insert(acc);
                if(flag){
                    //System.out.println("开户成功");
                    JOptionPane.showMessageDialog(null , "注册成功！");
                }else {
                    /*System.out.println("开户失败");*/
                    throw new RegisterException("注册失败!");
                }
            }
        }else{
            /*System.out.println("两次密码输入不一致！");*/
            throw new RegisterException("两次输入的密码不一致！");
        }
        return acc;
    }


    /**
     * 用户登录
     * @param id        账户ID
     * @param password  账户密码
     * @return
     */
    public Account login(Long id, String password) throws LoginException {
        if(id==null || password==null || password.equals("")){
           /* System.out.println("账户信息不能为空！");
            return null;*/
            throw new LoginException("用户ID和密码不能为空");
        }
        //查询
        Account acc = dao.selectOne(id, password);
        if(acc!=null){
            System.out.println("登录成功");
        }else{
            /*System.out.println("登录失败");*/
            throw new LoginException("登录失败");
        }
        return acc;
    }


    /**
     * 用户存款
     * @param id     账户ID
     * @param money  存的钱
     * @return
     */
   /* public Account deposit(Long id, double money) throws NotFoundException {
        if(id==null){
            *//*System.out.println("账户信息不能为空！");
            return null;*//*
            throw new NotFoundException("账户ID不能为空!");
        }
        //查询
        Account acc = dao.selectOne(id);
        if(acc!=null){
            acc.deposit(money);
        }else{
            *//*System.out.println("账户不存在！");*//*
            throw new NotFoundException("账户ID不能为空!");
        }
        return acc;
    }*/
    public Account deposit(Long id, double money) throws NotFoundException {
        if(id==null){
            /*System.out.println("账户信息不能为空！");
            return null;*/
            throw new NotFoundException("账户ID不能为空!");
        }
        //查询
        Account acc = dao.selectOne(id);
        if(acc!=null){
            acc.deposit(money);
            //文件操作时，需要更新
            dao.update(acc);
        }else{
            /*System.out.println("账户不存在！");*/
            throw new NotFoundException("账户ID不能为空!");
        }
        return acc;
    }


    /**
     * 用户取款
     * @param id        账户ID
     * @param password  账户密码
     * @param money     去的钱
     * @return
     */
   /* public Account withdraw(Long id, String password, double money) throws NotFoundException, BalanceNotEnoughException {
        if(id==null || password==null || password.equals("")){
            *//*System.out.println("账户信息不能为空！");
            return null;*//*
            throw new NotFoundException("账户ID不能为空!");
        }
        //查询
        Account acc = dao.selectOne(id, password);
        if(acc!=null){
            //编译看左边，运行看右边
            acc.withdraw(money);
        }else{
           *//* System.out.println("账户密码不匹配！");*//*
            throw new NotFoundException("当前账户的ID和密码不匹配");

        }
        return acc;
    }*/
    public Account withdraw(Long id, String password, double money) throws NotFoundException, BalanceNotEnoughException {
        if(id==null || password==null || password.equals("")){
            /*System.out.println("账户信息不能为空！");
            return null;*/
            throw new NotFoundException("账户ID不能为空!");
        }
        //查询
        Account acc = dao.selectOne(id, password);
        if(acc!=null){
            //编译看左边，运行看右边
            acc.withdraw(money);
            //文件操作时，需要更新
            dao.update(acc);
        }else{
            /* System.out.println("账户密码不匹配！");*/
            throw new NotFoundException("当前账户的ID和密码不匹配");

        }
        return acc;
    }


    /**
     * 设置透支额度
     * @param id        账户ID
     * @param password  账户密码
     * @param money     修改的透支额度
     * @return
     */
    public Account updateCeiling(Long id, String password, double money) throws TypeException, NotFoundException {
        if(id==null || password==null || password.equals("")){
            /*System.out.println("账户信息不能为空！");
            return null;*/
            throw new NotFoundException("账户ID不能为空!");
        }
        //查询
        Account acc = dao.selectOne(id, password);
        if(acc!=null){
            //判断账户的运行期类型是否是CreditAccount,若是则可以设置透支额度
            if(acc instanceof CreditAccount){
                ((CreditAccount) acc).setCeiling(money);
                //文件操作时，需要更新
                dao.update(acc);
            }else{
                /*System.out.println("当前账户不支持修改透支额度！");*/
                throw new TypeException("当前银行卡类型不支持修改透支额度");
            }
        }else{
            /*System.out.println("账户密码不匹配！");*/
            throw new NotFoundException("当前账户的ID和密码不匹配");
        }
        return acc;
    }


    /**
     * 转账功能
     * @param from  转出账户
     * @param passwordFrom  转出账号的密码
     * @param to    转入账户
     * @param money 转账的金额
     * @return
     */
    public boolean transfer(Long from, String passwordFrom, Long to, double money) throws NotFoundException, BalanceNotEnoughException {
        if(from==null || passwordFrom==null || passwordFrom.equals("") || to==null){
            /*System.out.println("转出或转入账户不能为空");
            return false;*/
            throw new NotFoundException("转出或转入账户不能为空");
        }
        if(from.equals(to)){
            /*System.out.println("转出或转入账户不能是同一个");
            return false;*/
            throw new NotFoundException("转出或转入账户不能是同一个");
        }
        Account accFrom = dao.selectOne(from, passwordFrom);
        if(accFrom!=null){
            Account accTo = dao.selectOne(to); //转入账户
            if(accTo!=null){
                //转账逻辑
                //一、判断转出账户余额是否充足
                //方式一，转账之后，需要进行account比对。比如SavingAccount中的取款return this(account)
                //方式一
                /*double oldBalance = accFrom.getBalance();
                double newBalance = accFrom.withdraw(money).getBalance();
                if(oldBalance==newBalance){
                    System.out.println("转出账户余额不足");
                    return false;
                }*/

                //
                //方式二,返回的是boolean，不需要return account进行比对
                //1.1储蓄账户
                if((accFrom instanceof SavingAccount) && accFrom.getBalance()<money){
                    /*System.out.println("转出账户余额不足");
                    return false;*/
                    throw new BalanceNotEnoughException("转出账户余额不足!");
                }
                //1.2信用账户
                if((accFrom instanceof CreditAccount) && accFrom.getBalance() + ((CreditAccount) accFrom).getCeiling() < money){
                    /*System.out.println("转出账户余额不足");
                    return false;*/
                    throw new BalanceNotEnoughException("转出账户余额不足!");
                }
                //二、余额充足的情况下，发生转账
                accFrom.withdraw(money);
                accTo.deposit(money);
                //文件操作时，需要更新
                dao.update(accFrom);
                dao.update(accTo);
                System.out.println("转账成功");
                return true;
            }else{
                /*System.out.println("转入账户不存在");*/
                throw new NotFoundException("转入账户不存在");
            }
        }else{
            /*System.out.println("转出账户不存在！");*/
            throw new NotFoundException("转出账户不存在");
        }
       /* return false;*/
    }


    /**
     * 贷  款
     * @param id     账户ID
     * @param money  贷款金额
     * @return
     */
    public Account requestLoan(Long id , double money) throws TypeException, NotFoundException {
        if(id==null){
           /* System.out.println("账户信息不能为空！");
            return null;*/
            throw new NotFoundException("账户ID不能为空!");
        }
        Account acc = dao.selectOne(id);
        if(acc!=null){
            if(acc instanceof Loanable){
                ((Loanable) acc).requestLoan(money);
                //文件操作时，需要更新
                dao.update(acc);
            }else{
                /*System.out.println("当前账户不支持贷款");*/
                throw new TypeException("当前账户类型不支持贷款！");
            }
        }else{
            /*System.out.println("账户不存在！");*/
            throw new NotFoundException("账户不存在");
        }
        return acc;
    }

    /**
     * 还贷款
     * @param id     账户ID
     * @param money  还款金额
     * @return
     */
    public Account payLoan(Long id , double money) throws TypeException, NotFoundException {
        if(id==null){
           /* System.out.println("账户信息不能为空！");
            return null;*/
            throw new NotFoundException("账户ID不能为空!");
        }
        Account acc = dao.selectOne(id);
        if(acc!=null){
            if(acc instanceof Loanable){
                //多态  编译看左边，运行看右边
                ((Loanable) acc).payLoan(money);
                //文件操作时，需要更新
                dao.update(acc);
            }else{
                /*System.out.println("当前账户不支持还贷");*/
                throw new TypeException("当前账户不支持还款！");
            }
        }else{
            /*System.out.println("账户不存在！");*/
            throw new NotFoundException("账户不存在");
        }
        return acc;
    }


    /**
     * 统计银行所有账户余额总数
     * @return
     */
    public double totalBalance(){
        double sum = 0.0;
        for (Account acc : dao.selectAll()) {
            if(acc!=null){
                sum+=acc.getBalance();
            }
        }
        return sum;
    }

    /**
     * 统计所有信用账户透支额度总数
     * @return
     */
    public double totalCeiling(){
        double sum = 0.0;
        for (Account acc : dao.selectAll()) {
            if(acc!=null){
                if(acc instanceof CreditAccount){
                    sum+=((CreditAccount) acc).getCeiling();
                }
            }
        }
        return sum;
    }


    /**
     * 统计所有账户贷款的总额
     * @return
     */
    public double totalLoanAmount(){
        double sum = 0.0;
        for (Account acc : dao.selectAll()) {
            if(acc!=null){
                if(acc instanceof LoanSavingAccount){
                    sum += ((LoanSavingAccount) acc).getLoanAmount();
                }
                if(acc instanceof LoanCreditAccount){
                    sum += ((LoanCreditAccount) acc).getLoanAmount();
                }
            }
        }
        return sum;
    }
    //当进行排名，则想到用TreeMap、TreeSet等
    //所有用户的资产排名
    public TreeSet<RankVO> rank(){
        TreeSet<RankVO> treeSet = new TreeSet<>();
        for(Account acc : dao.selectAll()){
            if(acc != null){
                String personId = acc.getPersonId();
                String personName = acc.getName();
                double total = acc.getBalance();
                //封装RankVO类
                RankVO rank = new RankVO(personId, personName, total);
                treeSet.add(rank);
                treeSet.add(new RankVO(acc.getPersonId(), acc.getName(), acc.getBalance()));
            }
        }
        return treeSet;
    }
    //通过身份证ID计算名下总资产
    public double totalBalancePersonID(String personID){
        double total = 0.0;
        for(Account acc : dao.selectAll()){
           if(acc != null && acc.getPersonId().equals(personID)){
               total += acc.getBalance();
           }
        }
        return total;
    }

}
